package org.ihelpuoo.orm;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathFactory;

import org.ihelpuoo.orm.handler.ObjectProcessor;
import org.ihelpuoo.orm.handler.conn.ConnectionProcessor;
import org.ihelpuoo.orm.handler.dom.ElementProcessor;
import org.ihelpuoo.orm.handler.http.HttpProcessor;
import org.ihelpuoo.orm.handler.map.MapProcessor;
import org.ihelpuoo.orm.handler.rs.ResultSetProcessor;
import org.ihelpuoo.orm.handler.xml.XmlFileProcessor;
import org.ihelpuoo.io.FilePatternFilter;
import org.ihelpuoo.io.FileUtils;
import org.ihelpuoo.lang.LoaderException;
import org.ihelpuoo.lang.StringUtils;
import org.w3c.dom.Document;

public class IIIManual
{
	private Map<Class, ObjectRule>	rules;
	private Charset					charset;				// the charset to parse the path.
	private DocumentBuilder			builder;
	private XPathFactory			xpathFactory;
	private III						factory;

	private ObjectProcessor			connectionProcessor;
	private ObjectProcessor			domProcessor;
	private ObjectProcessor			mapProcessor;
	private ObjectProcessor			resultSetProcessor;
	private ObjectProcessor			xmlFileProcessor;
	private HttpProcessor			httpProcessor;

	IIIManual(III factory, String path) throws Exception
	{
		this(factory,path,Charset.defaultCharset().name());
	}

	private IIIManual(III factory, String path, String charset) throws Exception
	{
		this.factory = factory;
		this.charset = Charset.forName(charset);
		connectionProcessor = new ConnectionProcessor(this);
		domProcessor = new ElementProcessor(this);
		mapProcessor = new MapProcessor(this);
		resultSetProcessor = new ResultSetProcessor(this);
		xmlFileProcessor = new XmlFileProcessor(this);
		httpProcessor = new HttpProcessor(this);
		xpathFactory = XPathFactory.newInstance();
		try
		{
			builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		}
		catch (ParserConfigurationException e)
		{
			new LoaderException(e);
		}
		this.load(path);
	}

	public III factory()
	{
		return factory;
	}

	/**
	 * Load all rule from the special path. Each rule is a properties
	 * file. 
	 * 
	 * @param path: a spacial path indicate the rules position. Path can
	 * 				be absolute path or a relative path. If the relative
	 * 				path, it will load from the classpath.
	 * @throws IOException 
	 * @throws ClassNotFoundException 
	 * @throws LoaderException 
	 */
	public void load(String path) throws IIIException
	{
		rules = new HashMap<Class, ObjectRule>();
		if (StringUtils.isBlank(path))
			return;
		File file = FileUtils.findFile(path,charset.name());
		if (null == file || !(file.exists() || !file.isDirectory()))
		{
			throw new IIIException("\"" + path + "\" should be a directory!");
		}
		FileFilter filter = FilePatternFilter.getInstance(".*.xml");
		File[] names = file.listFiles(filter);

		for (int i = 0; i < names.length; i++)
		{
			try
			{
				Document doc = this.dom().parse(names[i]);
				ObjectRule rule = ObjectRule.newInstance(factory,doc);
				rules.put(rule.getType(),rule);
			}
			catch (Exception e)
			{
				throw new IIIException("Fail in " + names[i],e);
			}
		}

	}

	public ObjectRule getRule(Class key) throws IIIException
	{
		ObjectRule v = rules.get(key);
		if (null == v)
			synchronized (this)
			{
				v = rules.get(key);
				if (null == v)
				{
					v = ObjectRule.newInstance(factory,key);
					rules.put(key,v);
				}
			}
		return v;
	}

	public DocumentBuilder dom()
	{
		return builder;
	}

	public XPathFactory xpath()
	{
		return xpathFactory;
	}

	public ObjectProcessor getConnectionProcessor()
	{
		return connectionProcessor;
	}

	public ObjectProcessor getDomProcessor()
	{
		return domProcessor;
	}

	public ObjectProcessor getMapProcessor()
	{
		return mapProcessor;
	}

	public ObjectProcessor getResultSetProcessor()
	{
		return resultSetProcessor;
	}

	public ObjectProcessor getXmlFileProcessor()
	{
		return xmlFileProcessor;
	}

	public HttpProcessor getHttpProcessor()
	{
		return httpProcessor;
	}

}
